﻿namespace EFS.RC.ClientPlatform
{
	public class Session : EFS.Common.Async.AsyncRoot
	{
		#region Locals

		// sync access
		private static object syncAccess = new object();

		// IM call
		private Microsoft.Rtc.Collaboration.InstantMessagingCall instantMessagingCall;
		private Microsoft.Rtc.Collaboration.InstantMessagingFlow instantMessagingFlow = null;

		// end point
		private EFS.RC.ClientPlatform.EndPoint endPoint = null;

		/// <summary>
		/// Status
		/// </summary>
		public enum SessionStatus { Idle, Started, Terminated }

		/// Status
		private SessionStatus status = SessionStatus.Idle;

		#endregion // Locals

		#region Delgates

		/// <summary>
		/// Shut down event delegate
		/// </summary>
		public delegate void Terminated();

		/// <summary>
		/// Terminated session delegate registrar
		/// </summary>
		public Terminated OnTerminated = null;

		/// <summary>
		/// Shut down event delegate
		/// </summary>
		public delegate void Established();

		/// <summary>
		/// Established session delegate registrar
		/// </summary>
		public Established OnEstablished = null;

		/// <summary>
		/// Message delegate
		/// </summary>
		public delegate void MessageReceived(EFS.RC.CC.Messages.Message message);

		/// <summary>
		/// Established session delegate registrar
		/// </summary>
		public MessageReceived OnMessageReceived = null;

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		public Session(EFS.RC.ClientPlatform.EndPoint endPoint)
		{
			// check
			EFS.Common.Verify.Reference(endPoint, "endPoint");

			// set
			this.endPoint = endPoint;
		}

		#endregion

		#region Usage

		/// <summary>
		/// Describe this object
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return "EFS.RC.C.Call";
		}

		/// <summary>
		/// Get status
		/// </summary>
		public SessionStatus Status
		{
			get { return status; }
		}

		/// <summary>
		/// Begin startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start-up
			return BeginAsyncProcess2(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Begin shutdown
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			EFS.EventLog.Log.LogInfo("BeginShutdown", this);

			// shutdown
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		/// <summary>
		/// Send message
		/// </summary>
		/// <param name="message"></param>
		/// <returns></returns>
		public System.IAsyncResult SendMessage(EFS.RC.CC.Messages.Message message)
		{
			// send message
			return instantMessagingFlow.BeginSendInstantMessage(
				new System.Net.Mime.ContentType(EFS.RC.CC.Messages.Message.ContentType),
				EFS.RC.CC.Messages.Message.SerializeToByteArray(message),
				OnInstantMessagingSendComplete,
				null);
		}

		#endregion

		#region Startup

		/// <summary>
		/// Call startup
		/// </summary>
		private void Startup()
		{
			// log
			EFS.EventLog.Log.LogInfo("Startup", this);

			// sync access
			lock (syncAccess)
			{
				if (status != SessionStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle Session can be started");
				}

				// started
				status = SessionStatus.Started;
			}

			// make conversation
			Microsoft.Rtc.Collaboration.Conversation conversation = endPoint.MakeConversation();
			conversation.StateChanged += OnConversationStateChanged;

			// create messaging object
			instantMessagingCall = new Microsoft.Rtc.Collaboration.InstantMessagingCall(conversation);
			instantMessagingCall.StateChanged += OnInstantMessagingCallStateChanged;
			instantMessagingCall.InstantMessagingFlowConfigurationRequested += OnInstantMessagingCallFlowConfigurationRequested;

			// call
			instantMessagingCall.BeginEstablish(
				EFS.RC.ClientPlatform.Settings.CcServerOfficeServiceSip,
				new Microsoft.Rtc.Collaboration.ToastMessage(EFS.RC.ClientPlatform.Settings.Toast),
				null,
				OnInstantMessagingCallEstablishComplete,
				null);
		}

		/// <summary>
		/// Flew configuration
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnInstantMessagingCallFlowConfigurationRequested(System.Object sender, Microsoft.Rtc.Collaboration.InstantMessagingFlowConfigurationRequestedEventArgs e)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnInstantMessagingCallFlowConfigurationRequested", this);

			// setup flow
			instantMessagingFlow = e.Flow;
			instantMessagingFlow.StateChanged += OnInstantMessagingFlowStateChanged;
			instantMessagingFlow.MessageReceived += InstantMessagingFlowMessageReceived;
		}

		/// <summary>
		/// Complete establish
		/// </summary>
		/// <param name="ar"></param>
		private void OnInstantMessagingCallEstablishComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnInstantMessagingCallEstablishComplete", this);

			try
			{
				// end
				instantMessagingCall.EndEstablish(ar);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// close
				BeginShutdown();
			}
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				// shutdown
				BeginShutdown();
			}
		}

		#endregion // Startup

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		protected override void OnException(System.Exception ex)
		{
 			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// close
			BeginShutdown();
		}

		/// <summary>
		/// Terminate complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnInstantMessagingSendComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnInstantMessagingSendComplete", this);
		}

		/// <summary>
		/// State changed for conversation
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnConversationStateChanged(object sender, Microsoft.Rtc.Signaling.StateChangedEventArgs<Microsoft.Rtc.Collaboration.ConversationState> args)
		{
			// check terminating
			if (args.State == Microsoft.Rtc.Collaboration.ConversationState.Terminating ||
				args.State == Microsoft.Rtc.Collaboration.ConversationState.Terminated)
			{
				// shutdown
				BeginShutdown();
			}
		}

		/// <summary>
		/// State changed for call
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnInstantMessagingCallStateChanged(object sender, Microsoft.Rtc.Collaboration.CallStateChangedEventArgs args)
		{
			// check terminating
			if (args.State == Microsoft.Rtc.Collaboration.CallState.Terminating ||
				args.State == Microsoft.Rtc.Collaboration.CallState.Terminated)
			{
				// shutdown
				BeginShutdown();
			}
		}

		/// <summary>
		/// Flow state changed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnInstantMessagingFlowStateChanged(object sender, Microsoft.Rtc.Collaboration.MediaFlowStateChangedEventArgs args)
		{
			// check terminating
			if (args.State == Microsoft.Rtc.Collaboration.MediaFlowState.Terminated)
			{
				// shutdown
				BeginShutdown();
			}
			else if (args.State == Microsoft.Rtc.Collaboration.MediaFlowState.Active)
			{
				try
				{
					// check delegate
					if (null != OnEstablished)
					{
						// call
						OnEstablished();
					}
				}
				catch (System.Exception ex)
				{
					// log
					EFS.EventLog.Log.LogException(ex, this);

					// shutdown
					BeginShutdown();
				}
			}
		}

		/// <summary>
		/// Message received
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void InstantMessagingFlowMessageReceived(object sender, Microsoft.Rtc.Collaboration.InstantMessageReceivedEventArgs e)
		{
			try
			{
				// get received data
				System.String messageString = e.MessageData.GetMessageBodyString();

				// test text body
				if (EFS.Common.Verify.String(messageString))
				{
					// get message
					EFS.RC.CC.Messages.Message message = EFS.RC.CC.Messages.Message.Deserialize(messageString);

					if (null != OnMessageReceived)
					{
						OnMessageReceived(message);
					}
				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

			}
		}

		#endregion

		#region Shutdown

		/// <summary>
		/// Shutdown
		/// </summary>
		private void Shutdown()
		{
			// lock local acess
			lock (syncAccess)
			{
				// check already terminated
				if (status != SessionStatus.Terminated)
				{
					// set terinated
					status = SessionStatus.Terminated;
				}
				else
				{
					// already terminated
					return;
				}
			}

			// log
			EFS.EventLog.Log.LogInfo("Shutdown", this);

			// shut down platform
			instantMessagingCall.BeginTerminate(OnInstantMessagingCallTerminateComplete, null);
		}

		/// <summary>
		/// Terminate complete
		/// </summary>
		/// <param name="ar"></param>
		void OnInstantMessagingCallTerminateComplete(System.IAsyncResult ar)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnInstantMessagingCallTerminateComplete", this);
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ap"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check delegate
				if (null != OnTerminated)
				{
					// call
					OnTerminated();
				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion // Shutdown
	}
}
